import uvicorn

from fastapi import FastAPI, Depends, HTTPException
from fastapi.params import Query
from fastapi.responses import Response
from pydantic import BaseModel
from pydantic.fields import Optional
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session, load_only, defer, joinedload

from database import db_session_factory
from models import Department, Employee

app = FastAPI()


def get_db_session():
    session = db_session_factory()
    try:
        yield session
        session.commit()
    except SQLAlchemyError as err:
        print(err)
        session.rollback()
    finally:
        session.close()


@app.get('/')
def say_hello():
    return {'code': 10000, 'message': 'Hello, FastAPI!'}


class Dept(BaseModel):
    no: int
    name: str
    location: str


class Emp(BaseModel):
    no: int
    name: str
    job: str
    manager_id: Optional[int]
    salary: int
    comm: Optional[int]
    department_id: int


@app.post('/api/depts', status_code=201)
# 依赖注入（函数回调式注入）
def create_dept(dept: Dept, session: Session = Depends(get_db_session)):
    """新增部门"""
    if not session.query(Department).get(dept.no):
        params = dept.dict()
        session.add(Department(**params))
        return {'code': 10000, 'message': '新增部门信息成功', 'dept': params}
    raise HTTPException(status_code=400, detail='指定的部门已存在')


@app.get('/api/depts')
def show_depts(session: Session = Depends(get_db_session)):
    """获取所有部门信息"""
    depts = session.query(Department).all()
    return {'code': 10000, 'depts': depts}


@app.get('/api/depts/{no}')
def show_one_dept(no: int, session: Session = Depends(get_db_session)):
    """获取指定部门信息"""
    department = session.query(Department).get(no)
    if department:
        return {'code': 10000, 'dept': department}
    raise HTTPException(status_code=404, detail='未找到对应的部门')


@app.put('/api/depts/{no}')
def edit_dept(no: int, dept: Dept, session: Session = Depends(get_db_session)):
    """更新部门信息"""
    department = session.query(Department).get(no)
    if department and department.no == dept.no:
        session.query(Department).filter(Department.no == no).update(dept.dict())
        return {'code': 10000, 'message': '更新部门信息成功', 'dept': dept}
    raise HTTPException(status_code=404, detail='未找到对应的部门')


@app.delete('/api/depts/{no}')
def delete_dept(no: int, session: Session = Depends(get_db_session)):
    """删除指定的部门"""
    department = session.query(Department).get(no)
    if department:
        session.delete(department)
    return Response(status_code=204)


# Query - 处理来自于URL最后问号后面的参数（查询参数）
# Path - 处理来自于URL路径中的参数（路径参数）
# Body - 处理来自HTTP请求消息体的参数（消息体参数）
@app.get('/api/emps')
def show_emps(
        deptno: int = Query(None),
        page: int = Query(1, gt=0, description='页码必须是大于0的整数'),
        size: int = Query(5, ge=5, le=50, description='页面大小必须在5到50之间'),
        session: Session = Depends(get_db_session)
):
    query = session.query(Employee)\
        .options(load_only(Employee.name, Employee.job))\
        .order_by(Employee.salary.desc())
    if deptno:
        query = query.filter(Employee.department_id == deptno)
    emps = query.offset((page - 1) * size).limit(size).all()
    return {'code': 10000, 'emps': emps}


@app.get('/api/emps/{no}')
def show_one_emp(no: int, session: Session = Depends(get_db_session)):
    emp = session.query(Employee)\
        .filter(Employee.no == no)\
        .options(defer('manager_id'), defer('department_id'))\
        .options(joinedload(Employee.department, innerjoin=True))\
        .one_or_none()
    if emp:
        return {'code': 10000, 'emp': emp}
    raise HTTPException(status_code=404, detail='没有对应的员工信息')


@app.post('/api/emps', status_code=201)
def create_emp(emp: Emp, session: Session = Depends(get_db_session)):
    if not session.query(Employee).get(emp.no):
        employee = Employee(**emp.dict())
        session.add(employee)
        return {'code': 10000, 'message': '添加员工信息成功', 'emp': emp}
    raise HTTPException(status_code=400, detail='员工信息已经存在')


@app.put('/api/emps/{no}')
def edit_emp(no: int, emp: Emp, session: Session = Depends(get_db_session)):
    employee = session.query(Employee).get(no)
    if employee:
        session.query(Employee).filter(Employee.no == no).update(emp.dict())
        return {'code': 10000, 'message': '更新员工信息成功', 'emp': emp}
    raise HTTPException(status_code=404, detail='没有对应的员工信息')


@app.delete('/api/emps/{no}')
def delete_emp(no: int, session: Session = Depends(get_db_session)):
    employee = session.query(Employee).get(no)
    if employee:
        session.delete(employee)
    return Response(status_code=204)


if __name__ == '__main__':
    uvicorn.run('main:app', reload=True)
